import java.io.IOException;
import java.io.InputStream;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;

import net.rim.device.api.math.Fixed32;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.EncodedImage;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.XYRect;
import net.rim.device.api.ui.component.Dialog;

/**
 * @author Yungoo Kim, Yong Woo Lee
 *
 */
public class GameField extends Field{
	
	//Rect area which holds the main gaming components
	XYRect mainGameArea;

	//Background image
	EncodedImage _background = 
		EncodedImage.getEncodedImageResource("back.jpg");
	
	//Column image
	EncodedImage _column = 
		EncodedImage.getEncodedImageResource("bar.png");
	
	//Cursor image
	Bitmap _cursor = 
		Bitmap.getBitmapResource("cursor.png");
	
	static int deviceWidth;
	static int deviceHeight;
	
	int numberOfMovements = 0;
	int numberOfDisks = 3;
	
	int cursorX = 0;
	int cursorY = 0;
	
	static final int COLUMNWIDTH = 10;
	static int COLUMNHEIGHT;
	static int sectionWidth;
	XYRect[] sec = new XYRect[3];
	
	DiskManager diskMng;
	
	boolean clicked = false;
	
	long startTime;
	long currentTime;
	
	// ad variables
	// the banners are going to be width wise
	public static final String BANNER_PRE = "banner";
	public static final int NUM_BANNERS = 5;
	public static final int BANNER_HEIGHT = 70;
	
	String url = "http://localhost";
	int port = 80;
	String[] bannerNames = new String[NUM_BANNERS];
	Bitmap img = null;
	HTTPAds imgGetter = null;
	
	public GameField(int width, int height, int a) {
		deviceWidth = width;
		deviceHeight = height;
		mainGameArea = new XYRect(0, BANNER_HEIGHT, deviceWidth, deviceHeight);
		_background = GameField.resizeImage(_background, deviceWidth, 
				deviceHeight - BANNER_HEIGHT);	
		
		startTime = System.currentTimeMillis();
		
		sectionWidth = (deviceWidth - COLUMNWIDTH*4)/3;
		COLUMNHEIGHT = deviceHeight/2;
		int x = 0;
		sec[0] = new XYRect(x+COLUMNWIDTH, 0,
				sectionWidth, deviceHeight+10);
		x = sectionWidth + COLUMNWIDTH;
		sec[1] = new XYRect(x+COLUMNWIDTH, 0,
				sectionWidth, deviceHeight+10);
		x = x + sectionWidth + COLUMNWIDTH;
		sec[2] = new XYRect(x+COLUMNWIDTH, 0,
				sectionWidth, deviceHeight+10);
		x = x + sectionWidth + COLUMNWIDTH;
		
		
		diskMng = new DiskManager(a, sectionWidth);
		
		// initialize the bannerNames to default
		for (int i = 0; i < NUM_BANNERS; i++){
			bannerNames[i] = "banners/banner" + (i + 1) + ".jpg"; 
		}
		imgGetter = new HTTPAds(this, url, port, bannerNames);
		// start the thread
		imgGetter.start();
	}
	
	int originalBlock_x;
	int originalBlock_y;
	
	///////////////////Action Listeners///////////////////////////////
	protected boolean navigationClick(int status, int time) {  
		for (int i = 0; i < diskMng.disk.length; i++){
			if (diskMng.disk[i].getLocation().contains(cursorX, cursorY)
					&& !diskMng.disk[i].isClicked() && isSmllest(diskMng.disk[i]) && !clicked){
				numberOfMovements++;
				originalBlock_y = diskMng.disk[i].getLocation().y;
				originalBlock_x = diskMng.disk[i].getLocation().x;
				diskMng.disk[i].toggleClicked();
				clicked = true;
			} else if (diskMng.disk[i].isClicked() && clicked){
				for (int k = 0; k < sec.length; k++){
					if (diskMng.disk[i].getLocation().
							intersects(sec[k])) {
						boolean t = positioner(diskMng.disk[i], sec[k], k);
						if (t){
							diskMng.disk[i].position = k;
						} else {
							diskMng.disk[i].location.x = originalBlock_x;
							diskMng.disk[i].location.y = originalBlock_y;
						}
						diskMng.disk[i].toggleClicked();
						clicked = false;
					}
				}
			}
		}
		this.invalidate();
		if (checkGameOver()){
			Dialog.alert("Win!");
			System.exit(0);
		}
		
		return true;  
	}  
	
	protected boolean navigationMovement(int dx, int dy,int status,int time) {
		cursorX += dx;
		cursorY += dy;
		
		if (cursorX < 0) cursorX = 0;
		if (cursorX > deviceWidth) cursorX = deviceWidth - _cursor.getWidth();
		if (cursorY < 0) cursorY = 0;
		if (cursorY > deviceHeight) cursorY = deviceHeight - _cursor.getHeight();

		for (int i = 0; i < diskMng.disk.length; i++){
			if (diskMng.disk[i].isClicked()){
				diskMng.disk[i].setLocation(cursorX - _cursor.getWidth()/2,
						cursorY - _cursor.getHeight()/2);
			}
		}
		this.invalidate();
		return true;
	}
	//////////////////////////////////////////////////////////////////
	
	private boolean positioner(Disk disk, XYRect section, int sectionIndex){
		int diskMid = (disk.getLocation().x + 
				disk.getLocation().X2())/2;
		int sectionMid = (section.x + section.X2())/2;
		int diskBot = disk.getLocation().Y2();
		int matchThisY = deviceHeight;
		
		for (int i = 0; i < diskMng.length(); i++){
			if (diskMng.disk[i].position == sectionIndex
					&& !diskMng.disk[i].equals(disk)){
				if (diskMng.disk[i].getLocation().y < matchThisY){
					matchThisY = diskMng.disk[i].getLocation().y;
					if (disk.sizeRank < diskMng.disk[i].sizeRank){
						return false;
					}
				}
			}
		}
		if (diskMid != sectionMid) {
			disk.getLocation().x = sectionMid 
			- disk.getLocation().width/2;
		}
		
		if (diskBot != matchThisY){
			disk.getLocation().y = matchThisY 
			- disk.getLocation().height;
		}
		return true;
	}
	
	private boolean isSmllest(Disk disk){
		for (int i = 0; i < diskMng.length(); i++){
			if(diskMng.disk[i].position == disk.position &&
					diskMng.disk[i].sizeRank > disk.sizeRank){
				return false;
			}
		}
		return true;
	}
	
	private boolean checkGameOver(){
		int pos = diskMng.disk[0].position;
		if (pos == 0){
			return false;
		}
		for (int i = 0; i < diskMng.length(); i++) {
			if (diskMng.disk[i].position != pos){
				return false;
			}
		}
		return true;
	}
	
	///////////////////Graphical Components///////////////////////////
	protected void layout(int width, int height) {
		setExtent(deviceWidth, deviceHeight);
		cursorX = deviceWidth/2;
		cursorY = deviceHeight/2;
	}
	
	protected void paint(Graphics g) {
		drawBackground(g);
		drawBanner(g);
//		g.setGlobalAlpha(150);
		drawColumns(g);
		drawDisks(g);
//		g.setGlobalAlpha(255);
		drawCursor(g);
		drawHUD(g);
	}
	
	//Draws the background which fills the entire screen then embed a background img
	//to sit on top of the background. On the image, is the interactive gaming screen.
	//On the rest, text representations of the game status(Time elapsed, number of disks, etc) 
	//should be presented.
	private void drawBackground(final Graphics g) {
		g.setBackgroundColor(Color.WHEAT);
		g.clear();
		g.drawBitmap(mainGameArea, _background.getBitmap(), 0, 0);
	}
	
	

	
	private void drawHUD(final Graphics g) {
		g.setColor(Color.BLUE);
		g.drawText("# of Moves " + Integer.toString(numberOfMovements)
				, 20, BANNER_HEIGHT);
		
		currentTime = System.currentTimeMillis();
		long timeElapsed = currentTime - startTime;
		int sec = (int)((timeElapsed%60000)/1000);
		int min = (int)(timeElapsed/60000);
		
		g.drawText("Time Elapsed : " + min + ":" + sec, 
				deviceHeight - 120, BANNER_HEIGHT);
		
		
		this.invalidate();
	}

	private void drawDisks(final Graphics g){
		for (int i = 0; i < diskMng.length(); i++) {
			Bitmap img = GameField.resizeImage(diskMng.disk[i].getImage(), 
					diskMng.disk[i].getLocation().width, 
					diskMng.disk[i].getLocation().height).getBitmap();
			g.drawBitmap(diskMng.disk[i].getLocation(), img, 0,0);
		}
	}
	
	private void drawColumns(final Graphics g) {
		g.drawBitmap(sec[0].x + sec[0].width/2 - COLUMNWIDTH/2,
				COLUMNHEIGHT, COLUMNWIDTH, 
				COLUMNHEIGHT, resizeImage(_column, COLUMNWIDTH, COLUMNHEIGHT).getBitmap(), 0, 0);
		g.drawBitmap(sec[1].x + sec[1].width/2 - COLUMNWIDTH/2,
				COLUMNHEIGHT, COLUMNWIDTH, 
				COLUMNHEIGHT, resizeImage(_column, COLUMNWIDTH, COLUMNHEIGHT).getBitmap(), 0, 0);
		g.drawBitmap(sec[2].x + sec[2].width/2 - COLUMNWIDTH/2,
				COLUMNHEIGHT, COLUMNWIDTH, 
				COLUMNHEIGHT, resizeImage(_column, COLUMNWIDTH, COLUMNHEIGHT).getBitmap(), 0, 0);
    }

		
	private void drawCursor(final Graphics g){
		g.drawBitmap(cursorX, cursorY, 
				_cursor.getWidth(), _cursor.getHeight(), 
				_cursor, 0, 0);
	}
	
	private void drawBanner(final Graphics g){
		if (img != null){
			// draw the banner
			g.drawBitmap(0, 0, mainGameArea.width, BANNER_HEIGHT, img, 0, 0);
		}
	}
	//////////////////////////////////////////////////////////////////
	
	public void onFocus() {
		this.invalidate();//Used when updating the GUI.
	}
	
	public void onUnfocus(){
		super.onUnfocus();
		this.invalidate();
	}
	
	public boolean isFocusable() {
		return true;
	}

	public void updateAd(Bitmap img) {
		this.img = img;
		// force ui thread to redraw the banner region
		this.invalidate(0,0,img.getWidth(), BANNER_HEIGHT);
	}
	
	//Image resizer.
	public static EncodedImage resizeImage(EncodedImage image, int newWidth, int newHeight) {
	    int scaleFactorX = Fixed32.div(Fixed32.toFP(image.getWidth()), Fixed32.toFP(newWidth));
	    int scaleFactorY = Fixed32.div(Fixed32.toFP(image.getHeight()), Fixed32.toFP(newHeight));
	    return image.scaleImage32(scaleFactorX, scaleFactorY);
	}

}

class HTTPAds extends Thread{
	private String url;
	private int port;
	//remember Screen for update callbacks
	private GameField gameField;
	final int SLEEPTIME = 60000;	// TODO: change back to 60000
	private String[] bannerNames; 

	public HTTPAds(GameField newGameScreen, String newUrl, int port, String[] names){
		url = newUrl;
		this.port = port;
		gameField = newGameScreen;
		bannerNames = names;
	}

	public void updateURL(String url){
		this.url = url;
	}
	
	public void run(){
	
		HttpConnection httpCon = null;
		InputStream is = null;
		final int BUFSIZE = 10000;
		int i = 0;

		// rid of / at the end
		if (url.endsWith("/"))
			url = url.substring(0, url.length()-1);
		
		try{
			while(true){
				String imgURL = url + ":" + port + "/" + bannerNames[i];
				
				// connect to the server and open a connection to the image file
				httpCon = (HttpConnection) Connector.open(imgURL);
				if(httpCon.getResponseCode() == HttpConnection.HTTP_OK){
					// open an input stream to download the image
					is = httpCon.openInputStream();
					// temp variable used to hold the downloaded raw data
					byte[] data = new byte[BUFSIZE];
					int length = 0;
					// used to append the downloaded raw data
					StringBuffer rawResponse = new StringBuffer();
					while (-1 != (length = is.read(data))) {
						rawResponse.append(new String(data, 0, length));
					}
					// get the combined data
					// in this case, image
					byte[] result = rawResponse.toString().getBytes();
					// encode the raw data to Bitmap so we can draw it on screen
					EncodedImage resultImg = EncodedImage.createEncodedImage(result, 0, result.length);
					EncodedImage resizedImg = GameField.resizeImage(resultImg, GameField.deviceWidth, GameField.BANNER_HEIGHT);
					final Bitmap img = resizedImg.getBitmap();

					// schedule the ui thread to draw the downloaded image
					UiApplication.getUiApplication().invokeLater(new Runnable()
					{
						public void run(){
							// pass the image to our ui thread to draw
							gameField.updateAd(img);
						}
					});
					
				}
				
				// close the connections
				// we need to do this because we cache nothing
				// also the url for each image changes since server doesn't do anything
				// server just holds the images
				if(is != null)
					is.close();
				if(httpCon != null)
					httpCon.close();

				// wait until next banner update
				try{	
					Thread.sleep(SLEEPTIME);
				}
				catch(InterruptedException ex){
					//Handle exception
				}
				
				//update the banner index
				i++;
				if (i >= GameField.NUM_BANNERS){
					i = 0;
				}
			}
		}catch(IOException ex){
			//Handle exception
		}
	}
}